package us.mmapp.services;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.auth.BasicScheme;
import org.json.JSONException;
import org.json.JSONObject;

import us.mmapp.entities.ObjectEntity;
import us.mmapp.interfaces.MediaSynchListener;
import us.mmapp.util.ApplicationData;
import us.mmapp.util.HttpManager;
import us.mmapp.util.ServicesUtils;
import us.mmapp.util.TextUtils;
import android.os.AsyncTask;
import android.util.Log;

public class ObjectSyncService
{
	private ObjectSyncService() {}
	
	private static long requestId;
	
	private static ArrayList<MediaSynchListener> listeners;
	
	public static void attach(MediaSynchListener listener)
	{
		if (listeners == null)
			listeners = new ArrayList<MediaSynchListener>();
		listeners.add(listener);
	}
	
	public static void deattach(MediaSynchListener listener)
	{
		if (listeners != null && listeners.contains(listener))
			listeners.remove(listener);
	}
	
	/**
	 * Upload object to the server.
	 * 
	 * @param username
	 * @param password
	 * @param obj
	 * @return the response
	 */
	private static JSONObject upload(String username, String password, ObjectEntity obj)
	{
		JSONObject ret = null;
		try
		{
			MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
		
			String prepare = URLEncoder.encode(obj.toJSON().toString(), "UTF-8");
	    	entity.addPart("data", new StringBody(prepare));
	    	
			HttpPost post = new HttpPost(ServicesUtils.SYNCH_OBJ);
			post.addHeader(BasicScheme.authenticate(
	    			new UsernamePasswordCredentials(username,TextUtils.md5(password)),
	    			"UTF-8",
	    			false)); 	
	    	post.setEntity(entity);
	   
	    	HttpResponse httpResponse = HttpManager.execute(post);
	    	HttpEntity responseEntity = httpResponse.getEntity();
	    	
            if (responseEntity != null)
            {
            	
                InputStream instream = responseEntity.getContent();
                String result = TextUtils.convertStreamToString(instream);           
                instream.close();
                // Try converting result to JSON
                Log.e("SYNCH", "Result: " + httpResponse);
                try
                {
					ret = new JSONObject(result);
				}
                catch (JSONException e)
				{
					Log.e("SYNCH", "Could not parse JSON.");
					ret = null;
				}
            }
		}
		catch (IOException e)
		{}
		return ret;
	}
	/**
	 * Announce the fail.
	 * 
	 * @param reqId the id of the request that was succesful.
	 * @param msg
	 */
	private static void fail(long reqId, String msg)
	{
		Log.v("SYNCH REQUEST", "FAIL ON " + reqId + " with " + msg);
		if (listeners != null)
			for (MediaSynchListener l : listeners)
				l.onMediaSynchFail(reqId, msg);
	}
	
	/**
	 * Announce the success.
	 * 
	 * @param reqId the id of the request that was succesful.
	 */
	private static void success(long reqId, long oid)
	{
		Log.v("SYNCH REQUEST", "SUCCESS ON " + reqId);
		if (listeners != null)
			for (MediaSynchListener l : listeners)
				l.onMediaSynchSuccess(reqId, oid);
	}
	
	
	/**
	 * Syncs an object with the server. The result is the id associated with the object
	 * on the server side, or -1 if failed.
	 * 
	 * @param obj
	 * @return the request Id
	 */
	public synchronized static long syncRequest(ObjectEntity obj)
	{
		++requestId;
		Log.v("SYNCH REQUEST", "" + obj.getTitle());
		AsyncTask<Object, Void, Object[]> t = new AsyncTask<Object, Void, Object[]>()
		{
			@Override
			protected Object[] doInBackground(Object... params)
			{
				Object[] ret = {upload((String)params[0], (String)params[1], (ObjectEntity)params[2]), params[3]};
				return ret;
			}		
			@Override
			protected void onPostExecute(Object[] result)
			{
				long requestId = Long.parseLong(((String)result[1]));
				if (result[0] == null)
				{
					fail(requestId, "Server cannot be reached.");
					return;
				}
				JSONObject res = (JSONObject) result[0];
				
				try
				{
					String msg = res.getString("error");
					fail(requestId, msg);
					return;
				} catch (JSONException e)
				{
					
				}		
				try
				{
					long oid = res.getLong("oid");
					success(requestId, oid);
				}
				catch (JSONException e)
				{
					fail(requestId, "Could not synch with the server.");
				}
			}
		};
		Object[] params = {ApplicationData.getUsername(), ApplicationData.getPassword(),
				obj, requestId + ""};
		t.execute(params);
		return requestId;
	}
}
